package com.bsi.rd.framework.zuul.filter;

import com.alibaba.fastjson.JSONObject;
import com.bsi.rd.framework.zuul.pojo.ResultJson;
import com.bsi.rd.framework.zuul.properties.PermitAllConfigProperties;
import com.bsi.rd.framework.zuul.service.PermissonService;
import com.bsi.rd.framework.zuul.utils.AttackTackleUtil;
import com.netflix.zuul.context.RequestContext;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;

import javax.servlet.*;
import javax.servlet.FilterConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

/**
 * DESCRIPTION:
 * 网关filter
 * 对于自定义放行的url直接进行放行
 * 对于携带token的请求 验证url对应权限及token对应的用户权限 权限不通过返回403 (之后RemoteTokenServices会验证token有效性)
 * 对于非自定义放行url及未携带token的请求之后会返回401
 *
 * @author zouyan
 * @create 2018-09-30 下午5:04
 * created by fuck~
 **/
public class HeaderFilter implements Filter {

    private static Logger logger = LogManager.getLogger(HeaderFilter.class);

    //非法字符错误状态码
    private static final Integer illicitCode = 600;

    @Autowired
    private PermitAllConfigProperties permitAllConfigProperties;

    @Autowired
    private PermissonService permissionService;

    @Autowired
    protected RedisTemplate redisTemplate;

    @Value("${userId}")
    private String TOKEN_USER_ID;

    @Value("${heads.tokenKey}")
    private String HEADS_TOKENKEY;


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // 首先检验是不是特殊字符攻击
        boolean b = AttackTackleUtil.doTackle((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
        if (!b) {
            logger.info("拦截到特殊字符请求");
            ResultJson reqErr = ResultJson.createError(illicitCode, "不允许携带特殊字符!");
            HttpServletResponse response = ((HttpServletResponse) servletResponse);
            response.setHeader("Content-type", "text/html;charset=UTF-8");
            response.setCharacterEncoding("UTF-8");
            response.getWriter().print(JSONObject.toJSON(reqErr));
            return;
        }
        String authorization = ((HttpServletRequest) servletRequest).getHeader(HEADS_TOKENKEY);
        String requestURI = ((HttpServletRequest) servletRequest).getRequestURI();
        logger.info(String.format("request URI : %s", requestURI));
        //对于自定义放行的url直接进行放行
        if (isPermitAllUrl(requestURI)) {
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }
        //非自定义放行url查看是否存在token 验证是否为jwt token 校验权限 权限不通过返回403
        String userId = parseJwtToken(authorization, servletResponse);
        RequestContext.getCurrentContext().addZuulRequestHeader(TOKEN_USER_ID, userId);
        // 权限校验（此处根据自己业务扩展）
        boolean checkPermission = permissionService.checkPermisson(userId, requestURI);
        if (!checkPermission) {
            logger.info("userId:{}, requestURI:{} 权限校验不通过", userId, requestURI);
            //权限校验不通过
            ((HttpServletResponse) servletResponse).sendError(HttpServletResponse.SC_FORBIDDEN);
            return;
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }

    private boolean isPermitAllUrl(String url) {
        return permitAllConfigProperties.isPermitAllUrl(url);
    }

    private boolean isJwtBearerToken(String token) {
        return StringUtils.countMatches(token, ".") == 2 && (token.startsWith("Bearer") || token.startsWith("bearer"));
    }

    /**
     * 解析jwtToken令牌
     *
     * @param token
     * @return userId
     */
    private String parseJwtToken(String token, ServletResponse servletResponse) throws IOException {
        if (StringUtils.isEmpty(token)) {
            logger.error("当前用户未携带token进行访问");
            ((HttpServletResponse) servletResponse).sendError(HttpServletResponse.SC_UNAUTHORIZED);
            return null;
        }
        boolean jwtBearerToken = isJwtBearerToken(token);
        if (!jwtBearerToken) {
            throw new IllegalArgumentException(token + "此值不为jwt令牌");
        }
        // 去除jwt令牌的头部与签名步
        String jwtBody = StringUtils.substringBetween(token, ".");
        // 获取userId
        String s = new String(Base64.decodeBase64(jwtBody));
        Map map = new com.fasterxml.jackson.databind.ObjectMapper().readValue(s, Map.class);
        return (String) map.get(TOKEN_USER_ID);
    }


}
